#include "tcp_server.h"

int server_fd;                                     // 服务端文件描述符
client_node client_arr[LIMIT_CONNECT];             // 用于存放连接的客户端信息
int client_num = 0;                                // 连接的客户端数量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 初始化线程互斥锁
client_node *client_head;                          // 客户端链表表头

int main(void) {
    client_head = init_client_node();  // 初始化链表
    init_socket();  // socket初始化

    pthread_t th;

    // 创建监控客户端连接线程
    if (pthread_create(&th, NULL, client_connect_action, NULL) != 0) {
        perror("pthread_create");
    }

    pthread_join(th, NULL);

    close(server_fd);//关闭服务端网络套接字

    return 0;
}

// socket初始化
int init_socket() {
    // 申请网络套接字文件
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        return 1;
    }
    printf("申请IPV4的TCP协议的网络套接字成功\n");

    int sw = 1;
    //设置套接字属性
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &sw, sizeof(int));//设置套接字可以地址复用

    //声明套接字sockaddr_in结构体变量，表示服务器
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(struct sockaddr_in));
    //初始化服务器端的套接字，并用htons和htonl将端口和地址转成网络字节序
    server_addr.sin_family = AF_INET;  // 指定协议是使用IPV4的协议
    server_addr.sin_port = htons(SERVER_PORT);  // 指定端口号,htons用短整型的方式转化为网络字节序
    //ip可是是本服务器的ip，也可以用宏INADDR_ANY代替，代表0.0.0.0，表明所有网卡地址
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) < 0) {
        perror("connect");
        return 1;
    }
    printf("绑定IP和端口到申请的网络套接字成功\n");

    //设置网络容量，开启网络监听
    if (listen(server_fd, LIMIT_CONNECT) < 0) {
        perror("listen");
        return 1;
    }
    printf("开启网络监听成功\n");
}

// 监控客户端连接
void *client_connect_action(void *args) {
    struct sockaddr_in client_addr;
    int addr_len = sizeof(struct sockaddr_in);
    int client_fd;
    while (1) {
        // 等待客户端连接,获取客户端信息
        client_fd = accept(server_fd, (struct sockaddr *)&client_addr, (socklen_t *)&addr_len);
        if (client_fd < 0) {
            perror("accept");
            return NULL;
        }

        // 客户端信息保存
        client_node *new_client = init_client_node();
        new_client->client_fd = client_fd;
        strcpy(new_client->ip, inet_ntoa(client_addr.sin_addr));
        new_client->port = htons(client_addr.sin_port);
        printf("客户端连接成功,IP = %s,port = %d,fd = %d\n", new_client->ip, new_client->port, new_client->client_fd);

        pthread_t th;
        // 每连接一个客户端创建一个服务端接收数据线程,用于接收客户端的信息并转发到其他客户端实现群聊
        if (pthread_create(&th, NULL, server_recv_action, (void *)new_client) != 0) {
            perror("pthread_create");
            continue;
        }
    }
}

// 服务器接收数据
void *server_recv_action(void *args) {
    client_node *client = (client_node *)(args);  // 客户端信息
    int rd_size;        // 读取数据的大小
    message msg;

    while (1) {
        memset(&msg, 0, sizeof(message));
        rd_size = recv(client->client_fd, &msg, sizeof(message), 0); // 从客户端接收数据
        printf("type=[%c],name=[%s],text=[%s]\n", msg.msg_type, msg.name, msg.text);
        switch (msg.msg_type) {
            case LOGIN: {
                client_login(client, &msg);
                break;
            }
            case AMOUNT: {
                client_get_client_amount(client, &msg);
                break;
            }
            case CLIENTS: {
                client_get_clients(client, &msg);
                break;
            }
            case SINGLE: {
                client_single(client, &msg);
                break;
            }
            case GROUP: {
                client_group_chat(client, &msg);
                break;
            }
            case QUIT: {
                printf("%s客户端登出连接\n", client->name);
                client_quit(client, &msg);
                return NULL;
            }
			case EXIT:{
				printf("客户端退出连接,IP = %s,port = %d,fd = %d\n", client->ip, client->port, client->client_fd);
				return NULL;
			}
            default:
                break;
        }
        // 客户端断开连接
        if (rd_size <= 0) {
            msg.msg_type = QUIT;
            strcpy(msg.name, client->name);
            strcpy(msg.text, "客户端断开连接");
            printf("%s客户端断开连接\n", msg.name);
            client_quit(client, &msg);
            return NULL;
        }

        display_all_client(client_head);  // 打印链表
    }
}

// 服务器群发数据
void server_broadcast_msg(client_node *client, message *msg) {
    int ret;
    // pthread_mutex_lock(&mutex);  // 加锁
    client_node *tmp = NULL;
    list_for_each_entry(tmp, &client_head->list, list) {
        if (client->client_fd != tmp->client_fd) {
            printf("name=[%s],type=[%c],name=[%s],text=[%s]\n", tmp->name, msg->msg_type, msg->name, msg->text);
            ret = send(tmp->client_fd, msg, sizeof(message), 0); // 向其他客户端发送数据
            if (ret < 0) { // 发送出错
                break;
            }
            if (ret == 0) {
                continue;
            }
        }
    }
    // pthread_mutex_unlock(&mutex);  // 解锁
}

// 服务器单发数据
void server_single_msg(client_node *client, message *msg) {
    printf("name=[%s],type=[%c],target_fd=[%d],text=[%s]\n", msg->name, msg->msg_type, msg->target_fd, msg->text);
    send(msg->target_fd, msg, sizeof(message), 0); // 向其他客户端发送数据
}

// 客户端登录
void client_login(client_node *client, message *msg) {
    pthread_mutex_lock(&mutex);  // 加锁
    strcpy(client->name, msg->name);
    strcpy(msg->text, "上线了");
    insert_client(client_head, client);
    server_broadcast_msg(client, msg);
    client_num++;
    pthread_mutex_unlock(&mutex);  // 解锁
}

// 客户端单聊
void client_single(client_node *client, message *msg) {
    pthread_mutex_lock(&mutex);  // 加锁
    server_single_msg(client, msg);
    pthread_mutex_unlock(&mutex);  // 解锁
}

// 客户端群聊
void client_group_chat(client_node *client, message *msg) {
    pthread_mutex_lock(&mutex);  // 加锁
    server_broadcast_msg(client, msg);
    pthread_mutex_unlock(&mutex);  // 解锁
}

// 客户端退出
void client_quit(client_node *client, message *msg) {
    pthread_mutex_lock(&mutex);  // 加锁
    strcpy(msg->text, "下线了");
    server_broadcast_msg(client, msg);
    close(client->client_fd);
    delete_client(client_head, client->client_fd);
    client_num--;
    pthread_mutex_unlock(&mutex);  // 解锁
}

// 客户端获取全部客户端数据
void client_get_client_amount(client_node *client, message *msg) {
    pthread_mutex_lock(&mutex);  // 加锁

    int ret;
    // 向目标客户端发送总客户端数量
    printf("client_num = %d\n", client_num);
    int_to_string(client_num, msg->text); // 10进制转字符串
    send(client->client_fd, msg, sizeof(message), 0);
    if (ret < 0) { // 发送出错
        perror("send");
        pthread_mutex_unlock(&mutex);  // 解锁
        return ;
    }

    pthread_mutex_unlock(&mutex);  // 解锁
}

// 客户端获取全部客户端数据
void client_get_clients(client_node *client, message *msg) {
    pthread_mutex_lock(&mutex);  // 加锁
    int ret;
    printf("client_num = %d\n", client_num);

    int_to_string(client_num, msg->text); // 10进制转字符串
    send(client->client_fd, msg, sizeof(message), 0);

    // send(client->client_fd, &client_num, sizeof(client_num), 0);
    if (ret < 0) { // 发送出错
        perror("send");
        pthread_mutex_unlock(&mutex);  // 解锁
        return ;
    }

    // 组装要发送的客户端数据
    client_data clients[client_num];
    int i = 0;
    memset(clients, 0, sizeof(clients));
    client_node *tmp = NULL;
    list_for_each_entry(tmp, &client_head->list, list) {
        strcpy(clients[i].name, tmp->name);
        clients[i].fd = tmp->client_fd;
        i++;
    }
printf("clients fd=%d\n",client->client_fd);
    // 向目标客户端发送所有客户端数据
    send(client->client_fd, clients, sizeof(clients), 0);
    if (ret < 0) { // 发送出错
        perror("send");
        pthread_mutex_unlock(&mutex);  // 解锁
        return ;
    }


    pthread_mutex_unlock(&mutex);  // 解锁
}

char *int_to_string(int num, char *str) { //10进制
    int i = 0;//指示填充str
    if (num < 0) { //如果num为负数，将num变正
        num = -num;
        str[i++] = '-';
    }
    //转换
    do {
        str[i++] = num % 10 + 48; //取num最低位 字符0~9的ASCII码是48~57；简单来说数字0+48=48，ASCII码对应字符'0'
        num /= 10;//去掉最低位
    } while (num); //num不为0继续循环

    str[i] = '\0';

    //确定开始调整的位置
    int j = 0;
    if (str[0] == '-') { //如果有负号，负号不用调整
        j = 1;//从第二位开始调整
        ++i;//由于有负号，所以交换的对称轴也要后移1位
    }
    //对称交换
    for (; j < i / 2; j++) {
        //对称交换两端的值 其实就是省下中间变量交换a+b的值：a=a+b;b=a-b;a=a-b;
        str[j] = str[j] + str[i - 1 - j];
        str[i - 1 - j] = str[j] - str[i - 1 - j];
        str[j] = str[j] - str[i - 1 - j];
    }

    return str;//返回转换后的值
}